package com.zhixiang.core.dao.impl;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang.StringUtils;
import org.hibernate.Criteria;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Projections;
import org.hibernate.engine.SessionFactoryImplementor;
import org.hibernate.hql.ast.QueryTranslatorImpl;
import org.hibernate.metadata.ClassMetadata;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import com.zhixiang.core.command.CriteriaCommand;
import com.zhixiang.core.command.FieldCommandImpl;
import com.zhixiang.core.command.QueryFilter;
import com.zhixiang.core.command.SortCommandImpl;
import com.zhixiang.core.dao.BaseDao;
import com.zhixiang.core.entity.TreeObj;
import com.zhixiang.core.util.Utils;
import com.zhixiang.core.web.paging.PagingBean;

public abstract class BaseDaoImpl<T, PK extends Serializable> extends
		HibernateDaoSupport implements BaseDao<T, PK> {
    
	@SuppressWarnings("unchecked")
    protected Class<?> persistType = (Class<T>)(
        (ParameterizedType)getClass()    //Class字节码
        .getGenericSuperclass())        //因为对于T.class我们无法获取，但是这个方法就能获取到父类的参数类型，返回值为ParameterizedType
        .getActualTypeArguments()[0];    //数组里第一个就是子类继承父类时所用类型;

	protected Map<String, String> querys = new HashMap<String, String>();

    @Autowired  
    public void setSessionFactoryOverride(SessionFactory sessionFactory)  
    {  
        super.setSessionFactory(sessionFactory);  
       
    }
    
    @SuppressWarnings("unchecked")
	public T get(PK paramPK) {
		return (T) getHibernateTemplate().get(this.persistType, paramPK);
	}

	public T save(T paramT) {
		getHibernateTemplate().saveOrUpdate(paramT);
		return paramT;
	}

	public T merge(T paramT) {
		getHibernateTemplate().merge(paramT);
		return paramT;
	}

	public void evict(T paramT) {
		getHibernateTemplate().evict(paramT);
	}
	
    @SuppressWarnings("unchecked")
    public T uniqueResult(final String queryString, final Object values[]) {
        return ((T) getHibernateTemplate().execute(new HibernateCallback() {
            public Object doInHibernate(Session paramSession)
                    throws HibernateException, SQLException {
                int i;
                Query localQuery = paramSession.createQuery(queryString);
                if (values != null){
                    for (i = 0; i < values.length; ++i){
                        localQuery.setParameter(i, values[i]);
                    }
                }
                return localQuery.uniqueResult();
            }
        }));
    }

	@SuppressWarnings("unchecked")
	public List<T> find(final String queryString, final Object values[],
			final int firstResult, final int pageSize) {
		return ((List<T>) getHibernateTemplate().execute(new HibernateCallback() {
			public Object doInHibernate(Session paramSession)
					throws HibernateException, SQLException {
				int i;
				Query localQuery = paramSession.createQuery(queryString);
				if (values != null)
					for (i = 0; i < values.length; ++i)
						localQuery.setParameter(i, values[i]);
				if (pageSize > 0)
					localQuery.setFirstResult(firstResult)
							.setMaxResults(pageSize).setFetchSize(pageSize);
				return localQuery.list();
			}
		}));
	}

	@SuppressWarnings("unchecked")
	public List<T> getAll() {
		return ((List<T>) getHibernateTemplate().execute(new HibernateCallback() {
			public Object doInHibernate(Session paramSession)
					throws HibernateException, SQLException {
				String str = "from "
						+ BaseDaoImpl.this.persistType.getName();
				Query localQuery = paramSession.createQuery(str);
				return localQuery.list();
			}
		}));
	}

	@SuppressWarnings("unchecked")
	public List<T> getAll(final PagingBean pb) {
		final String hql = "from " + this.persistType.getName();
		int i = getTotalItems(hql, null).intValue();
		pb.setTotalItems(i);
		return ((List<T>) getHibernateTemplate().execute(new HibernateCallback() {
			public Object doInHibernate(Session paramSession)
					throws HibernateException, SQLException {
				Query localQuery = paramSession.createQuery(hql);
				localQuery.setFirstResult(pb.getFirstResult()).setFetchSize(
						pb.getPageSize().intValue());
				localQuery.setMaxResults(pb.getPageSize().intValue());
				return localQuery.list();
			}
		}));
	}

	public Long getTotalItems(String paramString, final Object[] values) {
		int i = paramString.toUpperCase().indexOf(" ORDER BY ");
		if (i != -1)
			paramString = paramString.substring(0, i);
		QueryTranslatorImpl localQueryTranslatorImpl = new QueryTranslatorImpl(
				paramString, paramString, Collections.EMPTY_MAP,
				(SessionFactoryImplementor) getSessionFactory());
		localQueryTranslatorImpl.compile(Collections.EMPTY_MAP, false);
		final String sql = "select count(*) from ("
				+ localQueryTranslatorImpl.getSQLString() + ") tmp_count_t";
		Object localObject = getHibernateTemplate().execute(
				new HibernateCallback() {
					public Object doInHibernate(Session paramSession)
							throws HibernateException, SQLException {
						int i;
						SQLQuery localSQLQuery = paramSession
								.createSQLQuery(sql);
						if (values != null)
							for (i = 0; i < values.length; ++i)
								localSQLQuery.setParameter(i, values[i]);
						return localSQLQuery.uniqueResult();
					}
				});
		return new Long(localObject.toString());
	}

	@SuppressWarnings("unchecked")
	public List<T> findByHql(final String hql, final Object[] objs) {
		return ((List<T>) getHibernateTemplate().execute(new HibernateCallback() {
			public Object doInHibernate(Session paramSession)
					throws HibernateException, SQLException {
				int i;
				Query localQuery = paramSession.createQuery(hql);
				if (objs != null)
					for (i = 0; i < objs.length; ++i)
						localQuery.setParameter(i, objs[i]);
				return localQuery.list();
			}
		}));
	}
	@SuppressWarnings("unchecked")
	public List<T> findByHql(final String hql, final Object[] objs,final boolean isclear) {
		return ((List<T>) getHibernateTemplate().execute(new HibernateCallback() {
			public Object doInHibernate(Session paramSession)
					throws HibernateException, SQLException {
				if (isclear){					
					paramSession.clear();
				}
				int i;
				Query localQuery = paramSession.createQuery(hql);
				if (objs != null)
					for (i = 0; i < objs.length; ++i)
						localQuery.setParameter(i, objs[i]);
				return localQuery.list();
			}
		}));
	}

	@SuppressWarnings("unchecked")
	public List<T> findByHql(final String hql, final Object objs[],
			final int firstResult, final int pageSize) {
		return ((List<T>) getHibernateTemplate().execute(new HibernateCallback() {
			public Object doInHibernate(Session paramSession)
					throws HibernateException, SQLException {
				int i;
				Query localQuery = paramSession.createQuery(hql);
				localQuery.setFirstResult(firstResult).setMaxResults(pageSize);
				if (objs != null)
					for (i = 0; i < objs.length; ++i)
						localQuery.setParameter(i, objs[i]);
				return localQuery.list();
			}
		}));
	}

	public List<T> findByHql(String paramString, Object[] paramArrayOfObject,
			PagingBean paramPagingBean) {
		int i = getTotalItems(paramString, paramArrayOfObject).intValue();
		paramPagingBean.setTotalItems(i);
		return findByHql(paramString, paramArrayOfObject,
				paramPagingBean.getFirstResult(), paramPagingBean.getPageSize()
						.intValue());
	}

	public List<T> find(String paramString, Object[] paramArrayOfObject,
			PagingBean paramPagingBean) {
		int i = getTotalItems(paramString, paramArrayOfObject).intValue();
		paramPagingBean.setTotalItems(i);
		return find(paramString, paramArrayOfObject,
				paramPagingBean.getFirstResult(), paramPagingBean.getPageSize()
						.intValue());
	}

	public List<T> findByHql(String paramString) {
		return findByHql(paramString, null);
	}

	public void remove(PK paramPK) {
		getHibernateTemplate().delete(get(paramPK));
	}

	public void remove(T paramT) {
		getHibernateTemplate().delete(paramT);
	}

	public Object findUnique(final String hql, final Object values[]) {
		return getHibernateTemplate().execute(new HibernateCallback() {
			public Object doInHibernate(Session paramSession)
					throws HibernateException, SQLException {
				int i;
				Query localQuery = paramSession.createQuery(hql);
				if (values != null)
					for (i = 0; i < values.length; ++i)
						localQuery.setParameter(i, values[i]);
				return localQuery.uniqueResult();
			}
		});
	}

	public int getCountByFilter(final QueryFilter filter) {
		Integer localInteger = (Integer) getHibernateTemplate().execute(
				new HibernateCallback() {
					public Object doInHibernate(Session paramSession)
							throws HibernateException, SQLException {
						Criteria localCriteria = paramSession
								.createCriteria(BaseDaoImpl.this.persistType);
						for (int i = 0; i < filter.getCommands().size(); ++i) {
							CriteriaCommand localCriteriaCommand = (CriteriaCommand) filter
									.getCommands().get(i);
							if (!(localCriteriaCommand instanceof SortCommandImpl))
								localCriteria = localCriteriaCommand
										.execute(localCriteria);
						}
						localCriteria.setProjection(Projections.rowCount());
						return localCriteria.uniqueResult();
					}
				});
		if (localInteger == null)
			return new Integer(0).intValue();
		return localInteger.intValue();
	}

	public List<T> getAll(final QueryFilter queryFilter) {
		if (StringUtils.isNotEmpty(queryFilter.getFilterName()))
			return getAll2(queryFilter);
		int i = getCountByFilter(queryFilter);
		queryFilter.getPagingBean().setTotalItems(i);
		@SuppressWarnings("unchecked")
		List<T> localList = (List<T>) getHibernateTemplate().execute(
				new HibernateCallback() {
					public Object doInHibernate(Session paramSession)
							throws HibernateException, SQLException {
						Criteria localCriteria = paramSession
								.createCriteria(BaseDaoImpl.this.persistType);
						queryFilter.getAliasSet().clear();
						BaseDaoImpl.this.setCriteriaByQueryFilter(
								localCriteria, queryFilter);
						return localCriteria.list();
					}
				});
		if (queryFilter.isExport()) {
			SimpleDateFormat localSimpleDateFormat = new SimpleDateFormat(
					"yyyyMMddhhmmssSSS");
			String str = localSimpleDateFormat.format(new Date());
			queryFilter.getRequest().setAttribute("fileName", str);
			queryFilter.getRequest().setAttribute("__exportList", localList);
		}
		return localList;
	}

	public List<T> getAll2(QueryFilter queryfilter) {
		String s = ((String) querys.get(queryfilter.getFilterName())).trim();
		String s1 = null;
		String s2 = null;
		String s3 = null;
		int i = s.toUpperCase().indexOf(" ORDER BY ");
		int j = s.toUpperCase().indexOf(" WHERE ");
		if (i < 0)
			i = s.length();
		if (j < 0)
			j = s.length();
		if (j < 0) {
			s2 = " where 1=1 ";
		} else {
			s2 = s.substring(j + 7, i);
			logger.debug((new StringBuilder()).append("condition:").append(s2)
					.toString());
			Pattern pattern = Pattern.compile(" GROUP BY [\\w|.]+");
			Matcher matcher = pattern.matcher(s2.toUpperCase());
			if (matcher.find()) {
				s3 = s2.substring(matcher.start(), matcher.end());
				s2 = s2.replace(s3, " ");
			}
			s2 = (new StringBuilder()).append(" where (").append(s2)
					.append(")").toString();
		}
		String s4 = "";
		for (int k = 0; k < queryfilter.getCommands().size(); k++) {
			CriteriaCommand criteriacommand = (CriteriaCommand) queryfilter
					.getCommands().get(k);
			if (criteriacommand instanceof FieldCommandImpl) {
				s2 = (new StringBuilder())
						.append(s2)
						.append(" and ")
						.append(((FieldCommandImpl) criteriacommand)
								.getPartHql()).toString();
				continue;
			}
			if (!(criteriacommand instanceof SortCommandImpl))
				continue;
			if (!"".equals(s4))
				s4 = (new StringBuilder()).append(s4).append(",").toString();
			s4 = (new StringBuilder()).append(s4)
					.append(((SortCommandImpl) criteriacommand).getPartHql())
					.toString();
		}

		s1 = s.substring(0, j);
		if (queryfilter.getAliasSet().size() > 0) {
			int l = s1.indexOf(" FROM ");
			String s5 = null;
			if (l > 0) {
				String s6 = s1.substring(l + 6);
				String as[] = s6.split("[ ]");
				if (as.length > 1 && !as[1].toUpperCase().equals("ORDER")
						&& !as[1].toUpperCase().equals("JOIN"))
					s5 = as[1];
				if (s5 == null) {
					s5 = "vo";
					s1 = s1.replace(as[0], (new StringBuilder()).append(as[0])
							.append(" ").append(s5).toString());
				}
			}
			String s7 = "";
			for (Iterator<String> iterator = queryfilter.getAliasSet().keySet().iterator(); iterator
					.hasNext();) {
				String s8 = (String) iterator.next();
				s7 = (new StringBuilder()).append(s7).append(" join ")
						.append(s5).append(".").append(s8).append(" ")
						.append(s8).toString();
			}

			if (!"".equals(s7))
				s1 = (new StringBuilder()).append(s1).append(s7).toString();
		}
		s1 = (new StringBuilder()).append(s1).append(s2).toString();
		if (s3 != null)
			s1 = (new StringBuilder()).append(s1).append(s3).append(" ")
					.toString();
		if (!"".equals(s4))
			s1 = (new StringBuilder()).append(s1).append(" order by ")
					.append(s4).toString();
		else
			s1 = (new StringBuilder()).append(s1).append(s.substring(i))
					.toString();
		Object aobj[] = queryfilter.getParamValueList().toArray();
		int i1 = getTotalItems(s1, aobj).intValue();
		queryfilter.getPagingBean().setTotalItems(i1);
		if (logger.isDebugEnabled())
			logger.debug((new StringBuilder()).append("new hql:").append(s1)
					.toString());
		return find(s1, aobj, queryfilter.getPagingBean().getFirstResult(),
				queryfilter.getPagingBean().getPageSize().intValue());
	}

	public void flush() {
		getHibernateTemplate().flush();
	}

	private Criteria setCriteriaByQueryFilter(Criteria paramCriteria,
			QueryFilter paramQueryFilter) {
		for (int i = 0; i < paramQueryFilter.getCommands().size(); ++i){
		    paramCriteria = ((CriteriaCommand) paramQueryFilter.getCommands()
                .get(i)).execute(paramCriteria);
		}
			
		if(paramQueryFilter.getIsPage() == null){
		    paramCriteria.setFirstResult(paramQueryFilter.getPagingBean()
                .getFirstResult());
		    paramCriteria.setMaxResults(paramQueryFilter.getPagingBean()
                .getPageSize().intValue());
		}
		return paramCriteria;
	}

	public void setQuerys(Map<String, String> paramMap) {
		this.querys = paramMap;
	}

	public Long update(final String hql, final Object params[]) {
		return ((Long) getHibernateTemplate().execute(new HibernateCallback() {
			public Object doInHibernate(Session paramSession)
					throws HibernateException, SQLException {
				Query localQuery = paramSession.createQuery(hql);
				int i = 0;
				for (Object localObject2 : params)
					localQuery.setParameter(i++, localObject2);
				Integer integer = Integer.valueOf(localQuery.executeUpdate());
				return new Long(((Integer) integer).intValue());
			}
		}));
	}
	
	//ptli
    public List<T> findByParentId(Map<String,String> map){
//        String hql = "FROM "+ className + " WHERE parentId = ?";
        QueryFilter qf = new QueryFilter();
        for (Map.Entry<String, String> entry : map.entrySet()) {
            qf.addFilter(entry.getKey(), entry.getValue());
        }
        
        return getAll(qf);
    }
    
    /**
     * 前台得到tree节点集合
     */
    public List<TreeObj> getTreeObj(PK id , String displayName, Map<String,String> map){
          List<TreeObj> treelist = new ArrayList<TreeObj>();
          T obj = get(id);
          if(obj!=null){
              //获取实体类中ID的名字
              ClassMetadata meta =  
              getSessionFactory().getClassMetadata(obj.getClass());//仅适用有一个@ID的实体类
              String idName=meta.getIdentifierPropertyName();          
              Object sid=null;
              Object sname=null;
              try  
              {
                   sid = Utils.invokeMethod(obj,Utils.field2GetMethod(idName),null);
                   sname = Utils.invokeMethod(obj,Utils.field2GetMethod(displayName),null);
              }
              catch (Exception e)
              {
                  e.printStackTrace();
              }
            TreeObj tobj= new TreeObj();
            tobj.setId(sid.toString());
            tobj.setText(sname.toString());
            tobj.setExpanded(true);
            treelist.add(tobj);
            findchildren(tobj,idName,displayName,map);  
          }

        return treelist;
    }
    
    private void findchildren(TreeObj treeObj, String idName, String displayName,Map<String,String> map){
        
        map.put("Q_parentId_L_EQ", treeObj.getId());
        List<T> dList = findByParentId(map);
        if(dList != null) {
            List<TreeObj> treelist = new ArrayList<TreeObj>();
            for (int i = 0; i < dList.size(); i++ )
            {
                T obj =  dList.get(i);
                Object sid=null;
                Object sname=null;
                try
                {
                     sid = Utils.invokeMethod(obj,Utils.field2GetMethod(idName),null);
                     sname = Utils.invokeMethod(obj,Utils.field2GetMethod(displayName),null);
                }
                catch (Exception e)
                {
                    e.printStackTrace();
                }
                
                TreeObj tobj= new TreeObj();
                tobj.setId(sid.toString());
                tobj.setText(sname.toString());
                map.put("Q_parentId_L_EQ", sid.toString());
                List<T> dd = findByParentId(map);
                if(dd.size()==0){
                    tobj.setLeaf(true);
                }
                treelist.add(tobj);
            }
            treeObj.setChildren(treelist);
            
            for (int i = 0; i < treelist.size(); i++ )
            {
                TreeObj tbj = treelist.get(i);
                findchildren(tbj ,idName, displayName,map);
            }
        }
    }
}